
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 *
 */

package com.hlkj.pay.service.token.impl;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hlkj.framework.caches.service.RedisTemplateService;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.common.util.web.WebFrameworkUtils;
import com.hlkj.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.hlkj.pay.common.constants.AdminConstant;
import com.hlkj.pay.common.constants.RedisConstants;
import com.hlkj.pay.common.constants.TokenRedisConstant;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.infrastructure.mapper.token.TokenAuthLogMapper;
import com.hlkj.pay.infrastructure.mapper.token.TokenSecretMapper;
import com.hlkj.pay.infrastructure.model.token.TokenAuthLogDO;
import com.hlkj.pay.infrastructure.model.token.TokenSecretDO;
import com.hlkj.pay.service.token.ITokenService;
import com.hlkj.pay.util.RefererUtils;

import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author HlpayTeam
 * @date 2024/09/01 13:23
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TokenServiceImpl implements ITokenService {

    private final TokenSecretMapper tokenSecretMapper;
    private final TokenAuthLogMapper tokenAuthLogMapper;
    private final RedisTemplateService redisTemplateService;

    @Value("${spring.profiles.active:}")
    private String activeProfile;

    @Value("${hlkj.mer-appid.wx-mini}")
    private String wxMiniAppId;

    @Value("${hlkj.mer-domain.local}")
    private String merDomainLocal;

    @Value("${hlkj.mer-domain.test}")
    private String merDomainTest;

    @Value("${hlkj.mer-domain.prod}")
    private String merDomainProd;

    @Override
    public TokenSecretDO queryTenantInfoByAppId(String appId) {
        String dataSourceKey = tokenAppIdKey(appId);
        String value = (String) redisTemplateService.get(dataSourceKey);
        if (!StringUtils.isEmpty(value)) {
            if (log.isDebugEnabled()) {
                log.debug("queryTenantInfoByAppId cache appId:{}", appId);
            }
            return JsonUtils.parseObject(value, TokenSecretDO.class);
        }
        TokenSecretDO tenantSecretDO = queryTenantIdInfoByAppId(appId, null);
        if (tenantSecretDO == null) {
            return null;
        }
        redisTemplateService.set(dataSourceKey, JsonUtils.toJsonString(tenantSecretDO), TokenRedisConstant.TOKEN_APPID_INFO_EXPIRE_TIME);
        return tenantSecretDO;
    }

    @Override
    public TokenSecretDO queryTenantInfoByDomain() {
        try {
            HttpServletRequest request = WebFrameworkUtils.getRequest();
            // 判断客户端类型
            String clientType = request.getHeader(AdminConstant.CLIENT_TYPE);
            if(StrUtil.isNotEmpty(clientType)){
                // 判断是否微信小程序  微信小程序吗默认商户类型
                if(clientType.equalsIgnoreCase(CommonEnum.CLIENT_TYPE.WX_MINI.getDesc())){
                    String domain = null;
                    if(StrUtil.isEmpty(activeProfile)){
                        domain = merDomainLocal;
                    }else if(activeProfile.equals("uat")){
                        domain = merDomainTest;
                    }else if(activeProfile.equals("prod")){
                        domain = merDomainProd;
                    }else {
                        domain = merDomainLocal;
                    }
                    log.info("queryTenantInfoByDomain {},{}",clientType,domain);
                    TokenSecretDO tokenSecretDO = queryTenantIdInfoByAppId(wxMiniAppId,domain);
                    return tokenSecretDO;
                }
            }

            String domain = RefererUtils.domain();
            return queryTenantIdInfoByAppId(domain);
        }
        catch (Exception e) {
            return null;
        }
    }

    @CacheEvict(value = RedisConstants.TOKEN_DOMAIN, key = "#domain")
    public TokenSecretDO queryTenantIdInfoByAppId(String domain) {
        return selectOne(null, domain);
    }

    public TokenSecretDO queryTenantIdInfoByAppId(String appId, String domain) {
        return selectOne(appId, domain);
    }

    /**
     * 列表查询
     *
     * @param tenantAuthLogDO
     * @return
     */
    @Override
    public void addTenantAuthLog(TokenAuthLogDO tenantAuthLogDO) {
        tokenAuthLogMapper.insert(tenantAuthLogDO);
    }

    /**
     * tenant data source cache key
     * 
     * @param appId
     * @return
     */
    private String tokenAppIdKey(String appId) {
        StringBuilder sb = new StringBuilder(TokenRedisConstant.TOKEN_APPID_INFO);
        sb.append(appId);
        return sb.toString();
    }

    /**
     * tenant data source cache key
     * 
     * @param domain
     * @return
     */
    private String tokenDomainKey(String domain) {
        StringBuilder sb = new StringBuilder(TokenRedisConstant.TOKEN_DOMAIN_INFO);
        sb.append(domain);
        return sb.toString();
    }

    /**
     * 只查询一条
     *
     * @param appId
     * @param domain
     * @return
     */
    TokenSecretDO selectOne(String appId, String domain) {
        if(StrUtil.isEmpty(appId) && StrUtil.isEmpty(domain)){
            return null;
        }
        LambdaQueryWrapper<TokenSecretDO> wrapper = buildWrapper(appId, domain);
        wrapper.last(" limit 1");
        return tokenSecretMapper.selectOne(wrapper);
    }

    /**
     * 查询条件封装
     *
     * @param appId
     * @param domain
     * @return
     */
    LambdaQueryWrapper<TokenSecretDO> buildWrapper(String appId, String domain) {
        LambdaQueryWrapperX<TokenSecretDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eqIfPresent(TokenSecretDO::getAppId, appId);
        queryWrapper.eqIfPresent(TokenSecretDO::getDomain, domain);
        return queryWrapper;
    }

}
